﻿//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: RelationAttribute.cs
//
//  Description: Specifies a relation between two persistents.
// 
//--------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Reflection;

namespace Chili.Opf3.Relations
{
	/// <summary>
	/// This attribute is used to specify a relation between two or more persistent
	/// objects.
	/// </summary>
	/// <remarks>
	/// 	<para>This attribute is used on ObjectHolder classes
	///     (<see cref="Opf3.Relations.ObjectHolder{T}">ObjectHolder</see>,
	///     <see cref="Opf3.Relations.ObjectListHolder{T}">ObjectListHolder</see>,
	///     <see cref="Opf3.Relations.ObjectSetHolder{T}">ObjectSetHolder</see>) to specify the
	///     relation between the connected (related) persistent objects and the parent object
	///     (the persistent object that contains the attribute and the ObjectHolder class). The
	///     attribute supports one-to-one, one-to-many and many-to-many relations.</para>
	/// 	<para>To use the attribute you have to specify the ParentMember (which is a
	///     property or field of the parent object) and the ChildMember (which is one property or field of the
	///     child object). Usually one of the object member (property or field) is marked as foreign key in the
	///     storage.</para>
	/// </remarks>
	/// <example>
	///     The following example shows the usage of the RelationAttribute in combination with
	///     a ObjectSetHolder .
	///     <code lang="CS">
	/// [Persistent("USER", PoolSize = 50)]
	///  public class User
	///  {
	///         // ... Other properties and code.
	///  
	///         // The class contains an ObjectSetHolder and a RelationAttribute
	///         // that specifies the relation between the parent object and the 
	///         // related objects.
	///         [Relation("Id", "UserId")]
	///         private ObjectSetHolder&lt;Call&gt; _calls = new ObjectSetHolder&lt;Call&gt;();
	/// 
	///         // This relation specifies a many-to-many relation. Each user has different rights
	///         // and each right has different user.
	///         [Relation("Id = UserId", WeakPersistent = typeof(UserRight), 
	///             WeakRelations = "RightId = Id", Name = "Rights"]
	///         private ObjectSetHolder&lt;Right&gt; _rights = new ObjectSetHolder&lt;Right&gt;();
	///     
	///         public ObjectSet&lt;Call&gt; Calls
	///         {
	///             get { return _calls.InnerObject; }
	///             set { _calls.InnerObject = value; }
	///         }
	///  
	///         [Field("ID", Identifier = true, AllowDBNull = false)]
	///         public string Id
	///         {
	///             get { return _id; }
	///             set { _id = value; }
	///         }
	///         
	///         // ... Other properties and code.
	///  }
	///  
	///  [Persistent("CALLS", PoolSize = 20)]
	///  public class Call
	///  {
	///         // ... Other properties and code.
	///  
	///         [Field("USER_ID")]
	///         public string UserId
	///         {
	///             get { return _userId; }
	///             set { _userId = value; }
	///         }
	///  
	///         // ... Other properties and code.
	///  }
	/// 
	///  [Persistent("USER_RIGHTS")]
	///	 public class UserRight
	///  {
	///         // ... Other properties and code.
	///  
	///         [Field("USER_ID", AllowDBNull = false, Identifier = true)]
	///         public string UserId
	///         {
	///             get { return _userId; }
	///             set { _userId = value; }
	///         }
	///         
	///         [Field("RIGHT_ID", AllowDBNull = false, Identifier = true)]
	///         public string RightId
	///         {
	///             get { return _rightId; }
	///             set { _rightId = value; }
	///         }
	///  }
	/// 
	///  [Persistent("RIGHTS")]
	///  public class Right
	///  {
	///         // ... Other properties and code.
	///			
	///         [Field("ID", AllowDBNull = false, Identifier = true)]
	///         public string Id
	///         {
	///             get { return _rightId; }
	///             set { _rightId = value; }
	///         }
    ///         
	///         [Field("NAME", AllowDBNull = false)]
	///         public string Name
	///         {
	///             get { return _name; }
	///             set { _name = value; }
	///         }
	///  }
	///     </code>
	///     The following example shows the usage of the RelationAttribute with multiple columns as keys.
	///     <code lang="CS">
	/// [Persistent("USER", PoolSize = 50)]
	///  public class User
	///  {
	///         // ... Other properties and code.
	///  
	///         // The class contains an ObjectSetHolder and a RelationAttribute
	///         // that specifies the relation between the parent object and the 
	///         // related objects. Multiple columns are separated by a semi colon (;).
	///         // The relation is also given a name.
	///         [RelationAttribute("Id1 = UserId1; Id2 = UserId2", Name = "Calls")]
	///         private ObjectSetHolder&lt;Call&gt; _calls = new ObjectSetHolder&lt;Call&gt;();
	///     
	///         public ObjectSet&lt;Call&gt; Calls
	///         {
	///             get { return _calls.InnerObject; }
	///             set { _calls.InnerObject = value; }
	///         }
	///  
	///         [Field("ID1", Identifier = true, AllowDBNull = false)]
	///         public string Id1
	///         {
	///             get { return _id1; }
	///             set { _id1 = value; }
	///         }
	///         
	///         [Field("ID2", Identifier = true, AllowDBNull = false)]
	///         public string Id2
	///         {
	///             get { return _id2; }
	///             set { _id2 = value; }
	///         }
	///
	///         // ... Other properties and code.
	///  }
	///		</code>
	/// </example>
	[Serializable]
	[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
	public sealed class RelationAttribute : Attribute
	{
		private string _relations;
		private string _weakRelations;
		private PersistRelationships _persistRelationship = PersistRelationships.ParentFirst;
		private Type _weakPersistent;
		private PersistBehaviours _persistBehaviours = PersistBehaviours.None;
		private string _name;

		/// <summary>
		/// Creates a new instance of the <see cref="RelationAttribute">RelationAttribute Class</see>.
		/// </summary>
		private RelationAttribute()
		{
		}

		/// <summary>
		/// Creates a new instance of the <see cref="RelationAttribute">RelationAttribute Class</see>.
		/// </summary>
		/// <param name="parentMember">Specifies the name of the parent object member. The parent object member is 
		/// usually a property or field in the parent object.</param>
		/// <param name="childMember">Specifies the name of the child object member. The child object member is usually a
		/// property or field in the child object. The field of the object member should be marked as foreign key
		/// in the storage.</param>
		/// <exception cref="ArgumentNullException">ParentMember is null or childMember is null.</exception>
		public RelationAttribute(string parentMember, string childMember) : this()
		{
			if (parentMember == null)
				throw new ArgumentNullException("parentMember");
			if (childMember == null)
				throw new ArgumentNullException("childMember");

			_relations = string.Format("{0} = {1}", parentMember, childMember);
		}

		/// <summary>
		/// Creates a new instance of the <see cref="RelationAttribute">RelationAttribute Class</see>.
		/// </summary>
		/// <param name="relations">Specifies the relations between properties of different objects.</param>
		/// <exception cref="ArgumentNullException">Relations is null.</exception>
		public RelationAttribute(string relations) : this()
		{
			if (relations == null)
				throw new ArgumentNullException("relations");
			_relations = relations;
		}

		/// <summary>
		/// Specifies the relations between the weak persistent object and the target child object.
		/// </summary>
		/// <exception cref="ArgumentNullException">Value is null.</exception>
		public string WeakRelations
		{
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");
				_weakRelations = value;
			}
			get
			{
				return _weakRelations;
			}
		}

		/// <summary>
		/// Returns the relations specified in the constructor of the <see cref="RelationAttribute">RelationAttribute</see>
		/// class.
		/// </summary>
		internal string Relations
		{
			get { return _relations; }
		}

		/// <summary>
		/// Specifies the type of the weak persistent object. This object represents a weak entity required for
		/// many-to-many relations.
		/// </summary>
		public Type WeakPersistent
		{
			get { return _weakPersistent; }
			set { _weakPersistent = value; }
		}

		/// <summary>
		/// Specifies the
		/// <see cref="Opf3.Relations.PersistRelationships">PersistRelationship</see>
		/// between two persistent objects. This flag is only necessary when creating a 1:1
		/// relation. It specifies which object is saved first (the parent or the child object).
		/// Default is ParentFirst.
		/// </summary>
		public PersistRelationships PersistRelationship
		{
			get { return _persistRelationship; }
			set { _persistRelationship = value; }
		}

		/// <summary>Specifies the persist behaviour of the relation.</summary>
		/// <remarks>
		/// 	<para>By setting the behaviour you may only allow to insert or update relation.
		///     The default behaviour is to insert and update the relation in the storage.</para>
		/// </remarks>
		/// <value>Returns the persist behaviour of the relation.</value>
		public PersistBehaviours PersistBehaviours
		{
			get { return _persistBehaviours; }
			set { _persistBehaviours = value; }
		}

		/// <summary>
		/// Specifies the name for the relation. The name should be unique in a persistent type.
		/// </summary>
		public string Name
		{
			get { return _name; }
			set { _name = value; }
		}
	}
}
